package cn.htsyb.synergy.utils;

import jakarta.validation.constraints.NotNull;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.file.StandardOpenOption;
import java.util.EnumSet;
/**
 * @author lakeside hubin_yuan.410@hotmail.com
 * @version V1.0.0
 * @since 1.0
 * @date 2022/5/11
 */
public class IOUtil {
    public static final int BUFFER_SIZE = 4096;

    public static void nioTransferFile(@NotNull File srcFile, @NotNull File tagFile) throws IOException {
        try (
                FileChannel inFileChannel = FileChannel.open(srcFile.toPath(), EnumSet.of(StandardOpenOption.READ));
                FileChannel outFileChannel = FileChannel.open(tagFile.toPath(), EnumSet.of(StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE))) {
            inFileChannel.transferTo(0, inFileChannel.size(), outFileChannel);
        }
    }

    /**
     * 修改文件中的字节数据
     *
     * @param tagFile 要修改的文件
     * @param offset  偏移字节数
     * @return 是否修改成功
     * @throws Exception:文件读写中可能出的错
     */
    public static boolean changeFileByte(File tagFile, int offset, byte[] bits) throws Exception {

        try (RandomAccessFile raf = new RandomAccessFile(tagFile, "rw");
             FileChannel channel = raf.getChannel()) {
            MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_WRITE, offset, bits.length);
            buffer.put(bits);
            buffer.force();
            buffer.clear();
        }

        return true;
    }

    public static void nioTransferFileToStream(@NotNull WritableByteChannel writableByteChannel, @NotNull File file) throws IOException {
        try (FileChannel inFileChannel = FileChannel.open(file.toPath(), EnumSet.of(StandardOpenOption.READ))) {
            inFileChannel.transferTo(0, inFileChannel.size(), writableByteChannel);
        }
    }

    public static void nioTransferFileToStream(@NotNull WritableByteChannel writableByteChannel, @NotNull FileChannel fileChannel) throws IOException {
        fileChannel.transferTo(0, fileChannel.size(), writableByteChannel);
    }

    /**
     * <br/>注意：该方法会关闭通道，通道关闭即会导致流关闭
     *
     * @param file
     * @param out
     * @throws IOException
     */
    public static void nioTransferFileToStream(@NotNull OutputStream out, @NotNull File file) throws IOException {
        try (WritableByteChannel writableByteChannel = Channels.newChannel(out);
             FileChannel inFileChannel = FileChannel.open(file.toPath(), EnumSet.of(StandardOpenOption.READ))) {
            inFileChannel.transferTo(0, inFileChannel.size(), writableByteChannel);
        }
    }

    /**
     * <br/>注意：该方法会关闭通道，通道关闭即会导致流关闭
     *
     * @param in
     * @param file
     * @throws IOException
     */
    public static void nioTransferStreamToFile(@NotNull InputStream in, @NotNull File file) throws IOException {
        try (ReadableByteChannel readableByteChannel = Channels.newChannel(in);
             FileOutputStream fileOutStream = new FileOutputStream(file);
             FileChannel fileChannel = fileOutStream.getChannel()) {
            fileChannel.transferFrom(readableByteChannel, 0, in.available());
        }
    }

    public static void nioTransferStreamToFile(@NotNull ReadableByteChannel readableByteChannel, @NotNull File file) throws IOException {
        try (FileOutputStream fileOutStream = new FileOutputStream(file);
             FileChannel fileChannel = fileOutStream.getChannel()) {
            nioTransferStream(readableByteChannel, fileChannel);
        }
    }

    public static void nioTransferStreamToFile(@NotNull ReadableByteChannel readableByteChannel, @NotNull FileChannel fileChannel) throws IOException {
        nioTransferStream(readableByteChannel, fileChannel);
    }


    public static void nioTransferStream(@NotNull ReadableByteChannel readChannel, @NotNull WritableByteChannel writChannel) throws IOException {
        ByteBuffer buffer = ByteBuffer.allocate(10 * BUFFER_SIZE);
        while (readChannel.read(buffer) != -1) {
            // 切换为读状态
            buffer.flip();
            while (buffer.hasRemaining()) {
                writChannel.write(buffer);
            }
            buffer.clear();
        }
    }

    public static void transferStream(@NotNull InputStream in, @NotNull OutputStream out) throws IOException {
        byte[] buffer = new byte[BUFFER_SIZE];
        int bytesRead = -1;
        while ((bytesRead = in.read(buffer)) != -1) {
            out.write(buffer, 0, bytesRead);
        }
    }

}
